/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.to.aboomy.bannersample.indicator;

import com.to.aboomy.banner.Indicator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DependentLayout;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT;

/**
 * ThinWormIndicatorView
 *
 * @since 2021-08-09
 */
public class ThinWormIndicatorView extends Component implements Indicator,
        Component.DrawTask, Component.EstimateSizeListener {
    private static final int NUM_2 = 2;
    private static final int NUM_9 = 9;
    private static final int NUM_8 = 8;
    private static final int NUM_3 = 3;
    private static final int NUM_4 = 4;
    private static final int NUM_5 = 5;
    private static final int NUM_15 = 15;
    private static final int NUM_10 = 10;
    private static final int NUM_16 = 16;
    private static final int NUM_20 = 20;
    private static final int NUM_300 = 300;
    private static final float NUM_F_0_1 = 0.1f;
    private static final float NUM_F_0_2 = 0.2f;
    private static final float NUM_F_0_5 = 0.5f;
    private static final float NUM_F_2 = 2;
    private static final float NUM_F_10 = 10;
    private static final float NUM_F_8 = 8;
    private static final float NUM_F_25_5 = 25.5f;

    private Context context;
    private int pagerCount;

    // 指示器参数
    private float defaultIndicatorRadio;
    private float selectIndicatorRadio;
    private float defaultIndicatorScale = 1.0f;
    private float selectIndicatorScale = 1.0f;
    private float indicatorSpace;
    private Color selectIndicatorColor = Color.RED;
    private Color defaultIndicatorColor = Color.GRAY;
    private boolean isFirst;
    private int displayWidth;
    private RectFloat rectFloat;
    private int selectedPage = 0;
    private Paint slidePaint = new Paint();
    private Paint indicatorPaint = new Paint();
    private boolean isSquare;
    private float offset = 0;
    /**
     * 控制在banner中的位置
     */
    private DependentLayout.LayoutConfig params;

    /**
     * 构造
     *
     * @param context
     */
    public ThinWormIndicatorView(Context context) {
        this(context, null, null);
    }

    /**
     * 构造
     *
     * @param context
     * @param attrSet
     * @param styleName
     */
    public ThinWormIndicatorView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        displayWidth = display.getAttributes().width;
        slidePaint.setColor(selectIndicatorColor);
        slidePaint.setStyle(Paint.Style.FILL_STYLE);
        indicatorPaint.setStrokeWidth(NUM_3);
        indicatorPaint.setColor(defaultIndicatorColor);
        indicatorPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        indicatorPaint.setStyle(Paint.Style.FILL_STYLE);
        rectFloat = new RectFloat();
        defaultIndicatorRadio = AttrHelper.vp2px(NUM_F_8, context);
        selectIndicatorRadio = AttrHelper.vp2px(NUM_F_8, context);
        indicatorSpace = AttrHelper.vp2px(NUM_F_10, context);
        addDrawTask(this);
        setEstimateSizeListener(this);
    }

    @Override
    public void initIndicatorCount(int pagerCount1) {
        isSquare = true;
        this.pagerCount = pagerCount1;
        setVisibility(pagerCount > 1 ? VISIBLE : HIDE);
        postLayout();
        isFirst = true;
    }

    @Override
    public Component getView() {
        return this;
    }

    @Override
    public DependentLayout.LayoutConfig getParams() {
        if (params == null) {
            params = new DependentLayout.LayoutConfig(MATCH_CONTENT, MATCH_CONTENT);
            params.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_BOTTOM);
            params.setMarginBottom(AttrHelper.vp2px(NUM_10, context));
        }
        return params;
    }

    @Override
    public void setAutoStau(Boolean isAuto) {
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (pagerCount == 0) {
            return;
        }
        drawCircle(canvas, NUM_F_25_5);
    }

    private void drawCircle(Canvas canvas, float minY) {
        // 绘制初始状态圆
        drawPageCountCircle(canvas, minY);
        if (isFirst) {
            drawPagerCurrentCircle(canvas, minY);
            isFirst = false;
        } else {
            drawPagerCircle(canvas, minY);
        }
    }

    /**
     * 这里画page 圆
     *
     * @param canvas canvas
     * @param midY minY
     */
    private void drawPagerCircle(Canvas canvas, float midY) {
        float nowStartCx = indicatorStartX(selectedPage);
        int lastPage = getLastPageIndex(selectedPage);
        float startCx = indicatorStartX(lastPage);
        float instance = Math.abs(nowStartCx - startCx);
        float ratioIndicatorRadius = getDefaultScaleRadio();
        float left = startCx - ratioIndicatorRadius;
        float top = getHeight() / NUM_9 * NUM_8 + NUM_15 - defaultIndicatorRadio;
        float right = startCx + ratioIndicatorRadius;
        float bottom = getHeight() / NUM_9 * NUM_8 + NUM_15 + defaultIndicatorRadio;
        instance = instance - NUM_5;
        rectFloat.modify(left, top, right, bottom);
        if (isSquare) {
            if (lastPage == pagerCount - 1) {
                Temp temp = new Temp();
                temp.setLeft(left);
                temp.setRight(right);
                temp.setTop(top);
                temp.setBottom(bottom);
                leftSliderLast(temp, canvas, instance);
            } else {
                Temp temp = new Temp();
                temp.setLeft(left);
                temp.setRight(right);
                temp.setTop(top);
                temp.setBottom(bottom);
                leftSlider(temp, canvas, instance);
            }
        } else {
            if (lastPage == 0) {
                Temp temp = new Temp();
                temp.setLeft(left);
                temp.setRight(right);
                temp.setTop(top);
                temp.setBottom(bottom);
                leftSlider(temp, canvas, instance);
            } else {
                Temp temp = new Temp();
                temp.setLeft(left);
                temp.setRight(right);
                temp.setTop(top);
                temp.setBottom(bottom);
                rightSlider(temp, canvas, instance);
            }
        }
    }

    /**
     * 滑最后一个
     *
     * @param temp Temp
     * @param canvas Canvas
     * @param instance instance
     */
    private void leftSliderLast(Temp temp, Canvas canvas, float instance) {
        float left = temp.getLeft();
        float top = temp.getTop();
        float right = temp.getRight();
        float bottom = temp.getBottom();
        float ratioIndicatorRadius = getDefaultScaleRadio();
        float nowStartCx = indicatorStartX(selectedPage);
        if (offset < NUM_F_0_2) {
            rectFloat.modify(left, top, right, bottom);
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset > NUM_F_0_1 && offset < NUM_F_0_5) {
            rectFloat.modify(left - instance * (offset * NUM_2),
                    top + NUM_16 * (offset * NUM_2), right, bottom - NUM_16 * (offset * NUM_2));
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset < 1 && offset > NUM_F_0_5) {
            float top2 = top + NUM_16;
            float bottom2 = bottom - NUM_16;
            rectFloat.modify(left - instance, top2 - NUM_16 * ((offset - NUM_F_0_5) * NUM_2),
                    right - instance * ((offset - NUM_F_0_5) * NUM_2),
                    bottom2 + NUM_16 * ((offset - NUM_F_0_5) * NUM_2));
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset == 1) {
            float left2 = nowStartCx - ratioIndicatorRadius;
            float top2 = getHeight() / NUM_9 * NUM_8 + NUM_15 - defaultIndicatorRadio;
            float right2 = nowStartCx + ratioIndicatorRadius;
            float bottom2 = getHeight() / NUM_9 * NUM_8 + NUM_15 + defaultIndicatorRadio;
            rectFloat.modify(left2, top2, right2, bottom2);
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        }
    }

    /**
     * 向右滑动
     *
     * @param temp Temp
     * @param canvas Canvas
     * @param instance instance
     */
    private void rightSlider(Temp temp, Canvas canvas, float instance) {
        float left = temp.getLeft();
        float top = temp.getTop();
        float right = temp.getRight();
        float bottom = temp.getBottom();
        float ratioIndicatorRadius = getDefaultScaleRadio();
        float nowStartCx = indicatorStartX(selectedPage);
        if (offset < NUM_F_0_2) {
            rectFloat.modify(left, top, right, bottom);
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset > NUM_F_0_1 && offset < NUM_F_0_5) {
            rectFloat.modify(left - instance * (offset * NUM_2),
                    top + NUM_16 * (offset * NUM_2), right, bottom - NUM_16 * (offset * NUM_2));
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset < 1 && offset > NUM_F_0_5) {
            float top2 = top + NUM_16;
            float bottom2 = bottom - NUM_16;
            rectFloat.modify(left - instance, top2 - NUM_16 * ((offset - NUM_F_0_5) * NUM_2),
                    right - instance * ((offset - NUM_F_0_5) * NUM_2),
                    bottom2 + NUM_16 * ((offset - NUM_F_0_5) * NUM_2));
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset == 1) {
            float left2 = nowStartCx - ratioIndicatorRadius;
            float top2 = getHeight() / NUM_9 * NUM_8 + NUM_15 - defaultIndicatorRadio;
            float right2 = nowStartCx + ratioIndicatorRadius;
            float bottom2 = getHeight() / NUM_9 * NUM_8 + NUM_15 + defaultIndicatorRadio;
            rectFloat.modify(left2, top2, right2, bottom2);
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        }
    }

    /**
     * 向左滑动
     *
     * @param temp Temp
     * @param canvas canvas
     * @param instance instance
     */
    public void leftSlider(Temp temp,Canvas canvas, float instance) {
        float left = temp.getLeft();
        float top = temp.getTop();
        float right = temp.getRight();
        float bottom = temp.getBottom();
        float ratioIndicatorRadius = getDefaultScaleRadio();
        float nowStartCx = indicatorStartX(selectedPage);
        if (offset < NUM_F_0_2) {
            rectFloat.modify(left, top, right, bottom);
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset > NUM_F_0_1 && offset < NUM_F_0_5) {
            rectFloat.modify(left, top + NUM_16 * (offset * NUM_2),
                    right + instance * (offset * NUM_2), bottom - NUM_16 * (offset * NUM_2));
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset < 1 && offset > NUM_F_0_5) {
            float top2 = top + NUM_16;
            float bottom2 = bottom - NUM_16;
            rectFloat.modify(left + instance * ((offset - NUM_F_0_5) * NUM_2),
                    top2 - NUM_16 * ((offset - NUM_F_0_5) * NUM_2), right + instance,
                    bottom2 + NUM_16 * ((offset - NUM_F_0_5) * NUM_2));
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        } else if (offset == 1) {
            float left2 = nowStartCx - ratioIndicatorRadius;
            float top2 = getHeight() / NUM_9 * NUM_8 + NUM_15 - defaultIndicatorRadio;
            float right2 = nowStartCx + ratioIndicatorRadius;
            float bottom2 = getHeight() / NUM_9 * NUM_8 + NUM_15 + defaultIndicatorRadio;
            rectFloat.modify(left2, top2, right2, bottom2);
            canvas.drawRoundRect(rectFloat, defaultIndicatorRadio, defaultIndicatorRadio, slidePaint);
        }
    }

    /**
     * 画现在的圆
     *
     * @param canvas
     * @param midY
     */
    private void drawPagerCurrentCircle(Canvas canvas, float midY) {
        float startCx = indicatorStartX(selectedPage);
        float ratioIndicatorRadius = getDefaultScaleRadio();
        float left = startCx - ratioIndicatorRadius;
        float top = getHeight() / NUM_5 * NUM_4 + NUM_20 - defaultIndicatorRadio;
        float right = startCx + ratioIndicatorRadius;
        float bottom = getHeight() / NUM_5 * NUM_4 + NUM_20 + defaultIndicatorRadio;
        rectFloat = new RectFloat(left, top, right, bottom);
        rectFloat.modify(left, top, right, bottom);
        canvas.drawCircle(left + (right - left) / NUM_2, getHeight() / NUM_9 * NUM_8 + NUM_15,
                (right - left) / NUM_2 + NUM_2, slidePaint);
    }

    /**
     * 画默认圆
     *
     * @param canvas
     * @param minY
     */
    private void drawPageCountCircle(Canvas canvas, float minY) {
        for (int i1 = 0; i1 < pagerCount; i1++) {
            // 计算指示器起始位置
            float startX = indicatorStartX(i1);

            // 指示器边距设置
            float defaultScaleRadio = getDefaultScaleRadio();
            rectFloat = new RectFloat();
            float left = startX - defaultScaleRadio;
            float right = startX + defaultScaleRadio;
            canvas.drawCircle(left + (right - left) / NUM_2, getHeight() / NUM_9 * NUM_8 + NUM_15,
                    (right - left) / NUM_2, indicatorPaint);
        }
    }

    // 获取移动坐标点
    private float indicatorStartX(int index) {
        // 比较选中未选中半径取最大半径计算
        float defaultScaleRadio = getDefaultScaleRadio();
        float selectScaleRadio = getSelectScaleRadio();
        float scaleRadio = Math.max(defaultScaleRadio, selectScaleRadio);

        // 间距
        float centerSpace = scaleRadio * NUM_F_2 + indicatorSpace;
        float occupy = (displayWidth - centerSpace * pagerCount) / NUM_2;

        // 每个indicator位置
        float centerX = scaleRadio + centerSpace * index;
        return occupy + (scaleRadio - defaultScaleRadio) / NUM_2 + centerX;
    }

    /**
     * getLastPageIndex
     *
     * @param selectedPage1 int
     * @return int
     */
    private int getLastPageIndex(int selectedPage1) {
        int lastPageIndex = 0;
        if (isSquare) {
            int temp = 0;
            if (temp == selectedPage1) {
                lastPageIndex = pagerCount - 1;
            } else {
                lastPageIndex = selectedPage1 - 1;
            }
        } else {
            int temp = pagerCount - 1;
            if (temp == selectedPage1) {
                lastPageIndex = 0;
            } else {
                lastPageIndex = selectedPage1 + 1;
            }
        }

        return lastPageIndex;
    }

    @Override
    public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
        int width = Component.EstimateSpec.getSize(widthEstimateConfig);
        int height = Component.EstimateSpec.getSize(heightEstimateConfig);
        setEstimatedSize(
                Component.EstimateSpec.getChildSizeWithMode(width, width, Component.EstimateSpec.NOT_EXCEED),
                Component.EstimateSpec.getChildSizeWithMode(height, height, Component.EstimateSpec.NOT_EXCEED)
        );
        return true;
    }

    @Override
    public void onPageSliding(int position, float positionOffset, int positionOffsetPixels) {
        if (positionOffsetPixels > 0) {
            isSquare = true;
        } else {
            isSquare = false;
        }
    }

    @Override
    public void onPageSlideStateChanged(int i) {
    }

    @Override
    public void onPageChosen(int position) {
        selectedPage = position;
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setDuration(NUM_300);
        animatorValue.setValueUpdateListener((animatorValue1, v1) -> {
            offset = v1;
            this.invalidate();
        });
        animatorValue.start();
    }

    /**
     * 获取默认指示器大小
     *
     * @return float
     */
    private float getDefaultScaleRadio() {
        return defaultIndicatorScale * defaultIndicatorRadio;
    }

    /**
     * 选中指示器大小
     *
     * @return float
     */
    private float getSelectScaleRadio() {
        return selectIndicatorScale * selectIndicatorRadio;
    }

    /**
     * 储存参数的类，防止checkstyle
     *
     * @since 2021-08-13
     */
    class Temp {
        private float left;
        private float top;
        private float right;
        private float bottom;

        public float getLeft() {
            return left;
        }

        public void setLeft(float left) {
            this.left = left;
        }

        public float getTop() {
            return top;
        }

        public void setTop(float top) {
            this.top = top;
        }

        public float getRight() {
            return right;
        }

        public void setRight(float right) {
            this.right = right;
        }

        public float getBottom() {
            return bottom;
        }

        public void setBottom(float bottom) {
            this.bottom = bottom;
        }
    }
}
